﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ModbusLib.Base;

namespace ModbusLib
{
    public class ModbusSerial : ModbusMaster
    {
        public string PortName { get; set; } = "COM1";
        public int BaudRate { get; set; } = 9600;
        public Parity Parity { get; set; } = Parity.None;
        public int DataBits { get; set; } = 8;
        public StopBits StopBits { get; set; } = StopBits.One;
        public int ReadTimeout { get; set; } = 2000;
        public int ReadBufferSize { get; set; } = 4096;

        private SerialPort _serialPort;

        private CancellationTokenSource cts = new CancellationTokenSource();


        public bool Connected
        {
            get 
            {
                if (_serialPort == null)
                    throw new Exception("串口对象未初始化");
                return _serialPort.IsOpen;
            }
        }


        public ModbusSerial()
        {
            _serialPort = new SerialPort();
        }

        public override void Connect()
        {
            _serialPort.PortName = PortName;
            _serialPort.BaudRate = BaudRate;
            _serialPort.Parity = Parity;
            _serialPort.StopBits = StopBits;
            _serialPort.DataBits = DataBits;
            _serialPort.ReadTimeout = ReadTimeout;
            _serialPort.ReadBufferSize = ReadBufferSize;

            _serialPort.Open();

            // 这里可以启动一个后台线程进行队列数据的获取和请求
            Task.Run(async () =>
            {
                AsyncModel model = null;
                Exception _ex = null;
                byte[] datas = null;
                try
                {
                    while (!cts.IsCancellationRequested)
                    {
                        if (_serialPort == null || AsyncModels.Count == 0)
                        {
                            await Task.Delay(100);
                            continue;
                        }

                        model = AsyncModels.First();
                        datas = this.SendAndReceive(model.ReqBytes, model.RespLen);
                        datas = this.Check(datas, model.RespLen);
                    }
                }
                catch (Exception ex)
                {
                    _ex = ex;
                }
                finally
                {
                    if (model != null)
                    {
                        model.Completed.Invoke(datas, model.Handler, _ex);
                        AsyncModels.RemoveAll(m => m.Handler == model.Handler);
                    }
                }
            });
        }

        protected virtual byte[] Check(byte[] resp, int len) { return null; }


        public override void Disconnect()
        {
            _serialPort?.Close();
            cts.Cancel();
        }

        private static readonly object _lock = new object();

        protected override byte[] SendAndReceive(byte[] bytes, int len)
        {
            lock (_lock)
            {
                if (!_serialPort.IsOpen)
                    throw new Exception("串口对象未连接");
                _serialPort.Write(bytes, 0, bytes.Length);

                List<byte> respBytes = new List<byte>();
                try
                {
                    while (respBytes.Count < len)
                    {
                        // 读取超时时间 2000ms
                        respBytes.Add((byte)_serialPort.ReadByte());
                    }
                }
                catch (Exception ex)
                {
                    // 超时异常
                }
                return respBytes.ToArray();
            }
        }

        protected byte[] CRC16(List<byte> data)
        {
            if (data == null || !data.Any())
                throw new ArgumentException("");

            //运算
            ushort crc = 0xFFFF;
            for (int i = 0; i < data.Count; i++)
            {
                crc = (ushort)(crc ^ (data[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                }
            }
            byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
            byte lo = (byte)(crc & 0x00FF);         //低位置

            return new byte[] { hi, lo };
        }

        protected byte LRC(byte[] value)
        {
            if (value == null) return 0x00;

            int sum = 0;
            for (int i = 0; i < value.Length; i++)
            {
                sum += value[i];
            }

            sum = sum % 256;  // 只拿低位的一个字节数据
            sum = 256 - sum;

            return (byte)sum;
        }
    }
}
